import { LinearGradient, RadialGradient, StopObject, SvgColor } from "./Color";
import { CircleObject, ElementObjectType, EllipseObject, FiltersObject, GroupObject, PathDrawItem, PathObject, RectObject, TextObject, UseObject } from "./ElementObject";
import {
    AnimateAttribute,
    EffctEnum,
    Filters,
    MultipleValueListObject,
    MultipleValueObject,
    SelectObject, StageObecjArray,
    TransformType, panelTitle
} from "./ObjectUtils";
/**
 * 舞台组件
 */
export class StageObject {
    public id: string = '';
    public name: string = 'element';
    public dom: HTMLElement | undefined = undefined;
    public initX = 0;
    public initY = 0;
    public x: number = 0;
    public y: number = 0;
    public type = ElementObjectType.svg;
    public children = new StageObecjArray<StageObject>;
    public defs: StageObject[] = [];
    public hasChildren = true;
    public path: PathDrawItem[] = [];
    public closed: boolean = false;
    public parent: StageObject | null = null
    public transform: any;
    public filters: FilterMultipleValueObject = new FilterMultipleValueObject;
    public editPoints = false;
    public clipPath: SelectObject = new SelectObject('', []);
    public keys = [];
    public node?: Element;
    public userCreate = true
    constructor() {
        this.id = this.createID();
    }
    /**
     * 路径字符描述
     */
    get pathString() {
        return '';
    }
    createID() {
        let str = '';
        for (let i = 0; i < 8; i++) {
            const r = Math.floor(Math.random() * 26) + 97;
            str += String.fromCharCode(r);
        }
        return str + '_' + Date.now() + '_' + Math.round(Math.random() * 10000);
    }
    /**获取指定属性 */
    getValue(key: string) {
        return Reflect.get(this, key);
    }
    closePath() {

    }
    pathToString() {
        return ''
    }
    /**
     * 添加动画
     * @param t 
     */
    addAnimate(t: EffctEnum) {
        let child: StageObject | null = null
        switch (t) {
            case EffctEnum.animate:
                child = new AnimateObject();
                const animate = child as AnimateObject;
                animate.timeLine.set('0', '0')
                animate.timeLine.set('1', '60')
                break
            case EffctEnum.animateMotion:
                child = new AnimateMotionObject();
                break
            case EffctEnum.animateTransform:
                child = new AnimateTransformObject();
                break
        }
        if (child) {
            child.name += this.children.length;
            child.parent = this;
            this.children.push(child);
        } else {
            throw Error('暂不支持，开发中...');
        }
    }
    removeAnimate(id: string) {
        const index = this.children.findIndex(el => el.id == id);
        if (index > -1) {
            this.children.splice(index, 1);
        }
    }
    toCode() {
        return ''
    }
    public static fromNode(_node: SVGElement) {
        return new StageObject()
    }
    setBaseAttr(node: SVGElement) {
        const transform = node.getAttribute('transform');
        const transformOrigin = node.getAttribute('transform-origin');
        if (transform) {
            this.transform = TransformObject.fromTransformString(transform, transformOrigin);
        }
        const id = node.getAttribute('id');
        if (id) {
            this.id = id;
        }
    }
}

/**
 * SVG 组件
 */
export class SvgObject extends StageObject {
    public xmlns = 'http://www.w3.org/2000/svg';
    @panelTitle('宽度')
    public width: number = 500;
    @panelTitle('高度')
    public height: number = 500;
    public background: SvgColor = new SvgColor('#fff');
    public version = '1.1';
    public view = {
        x: 0,
        y: 0,
        width: 500,
        height: 500
    }
    constructor() {
        super();
        this.name = 'svg'
        this.background.mono = true
    }
    public get viewBox(): string {
        return `${this.view.x} ${this.view.y} ${this.view.width} ${this.view.height}`
    }

    public static fromCode(code: string) {
        const parser = new DOMParser()
        const dom = parser.parseFromString(code, 'image/svg+xml')
        const svg = SvgObject.fromNode(dom.documentElement as any);
        const defs = svg.children.filter(el => el.type === ElementObjectType.defs);
        for (const def of defs) {
            svg.defs.push(...def.children);
        }
        const norm = svg.children.filter(el => el.type !== ElementObjectType.defs);
        svg.children = norm;
        return svg
    }

    public parseNode(parent: StageObject, node: Element) {
        let curNode: StageObject | null = null;
        if (node.nodeName == 'defs') {
            curNode = DefsObject.fromNode(node as SVGDefsElement);
            parent.children.push(curNode);
        } else if (node.nodeName == 'rect') {
            curNode = RectObject.fromNode(node as SVGRectElement);
            parent.children.push(curNode);
        } else if (node.nodeName == 'ellipse') {
            curNode = EllipseObject.fromNode(node as SVGEllipseElement);
            parent.children.push(curNode);
        } else if (node.nodeName == 'circle') {
            curNode = CircleObject.fromNode(node as SVGEllipseElement);
            parent.children.push(curNode);
        } else if (node.nodeName == 'path') {
            curNode = PathObject.fromNode(node as SVGPathElement);
            parent.children.push(curNode);
        } else if (node.nodeName == 'g') {
            curNode = GroupObject.fromNode(node as SVGGElement);
            parent.children.push(curNode);
        } else if (node.nodeName == 'text') {
            curNode = TextObject.fromNode(node as SVGTextElement);
            parent.children.push(curNode);
        } else if (node.nodeName == 'use') {
            curNode = UseObject.fromNode(node as SVGUseElement);
            parent.children.push(curNode);
        } else if (node.nodeName == 'linearGradient') {
            curNode = LinearGradient.fromNode(node as SVGLinearGradientElement);
            parent.children.push(curNode);
        } else if (node.nodeName == 'radialGradient') {
            curNode = RadialGradient.fromNode(node as SVGRadialGradientElement);
            parent.children.push(curNode);
        } else if (node.nodeName == 'stop') {
            curNode = StopObject.fromNode(node as SVGRadialGradientElement);
            parent.children.push(curNode);
        } else if (node.nodeName == 'filter') {
            curNode = FiltersObject.fromNode(node as SVGFilterElement);
            parent.children.push(curNode);
        } else if (node.nodeName == 'animate') {
            curNode = AnimateObject.fromNode(node as SVGAnimateElement);
            parent.children.push(curNode);
        } else if (node.nodeName == 'animateMotion') {
            curNode = AnimateMotionObject.fromNode(node as SVGAnimateMotionElement);
            parent.children.push(curNode);
        } else if (node.nodeName == 'animateTransform') {
            curNode = AnimateTransformObject.fromNode(node as SVGAnimateMotionElement);
            parent.children.push(curNode);
        }

        if (!curNode) {
            return
        }
        const children = node.children;
        for (const child of children) {
            this.parseNode(curNode, child);
        }
    }
    static fromNode(node: SVGElement) {
        const svg = new SvgObject();
        svg.node = node;
        const viewBox = node.getAttribute('viewBox');
        if (viewBox) {
            const ar = viewBox.includes(',') ? viewBox.split(',') : viewBox.split(' ');;
            svg.view.x = Number(ar[0]);
            svg.view.y = Number(ar[1]);
            svg.view.width = Number(ar[2]);
            svg.view.height = Number(ar[3]);
        }
        const width = node.getAttribute('width');
        if (width) {
            svg.width = Number(width);
        }
        const height = node.getAttribute('height');
        if (height) {
            svg.height = Number(height);
        }

        const children = node.children;
        for (const child of children) {
            svg.parseNode(svg, child);
        }

        return svg
    }
}

/**
 * 预制 组件
 */
export class DefsObject extends StageObject {
    public type: ElementObjectType = ElementObjectType.defs;
    constructor() {
        super();
        this.name = '预制集';
    }
    static fromNode(_node: SVGElement) {
        const defs = new DefsObject();
        return defs;
    }
}

export class AnimateComObject extends StageObject {
    @panelTitle('变换属性')
    public attributeName: AnimateAttribute | string = 'x';
    @panelTitle('变换次数')
    public repeatCount: number | string = 'indefinite';
    @panelTitle('时长/s')
    public duration: number = 5;
    public subtype = 'rotate'

    public timeLine = new Map<string, string>();
    constructor() {
        super();
        this.hasChildren = false;
        this.name = '基础动画';
    }

    public get keyTimes() {
        let ar: string[] = [];
        for (const k of this.timeLine.keys()) {
            ar.push(k);
        }

        ar = ar.sort();
        return ar.join(';')
    }

    public getattributeName() {
        return typeof this.attributeName === 'string' ? this.attributeName : this.attributeName.value
    }

    public get values() {
        const ar: any[] = [];
        const keys = this.keyTimes.split(';');
        let origin = 0
        if (this.parent) {
            switch (this.getattributeName()) {
                case 'x':
                    origin = this.parent.x;
                    break;
                case 'y':
                    origin = this.parent.y;
                    break;
            }
        }

        for (const k of keys) {
            const val = this.timeLine.get(k);
            if (val !== undefined) {
                if (origin != 0) {
                    ar.push(origin + Number(val));
                } else {
                    ar.push(val);
                }
            }
        }
        return ar.join(';')
    }

    static fromNode(node: SVGElement) {
        const animate = new AnimateComObject();
        animate.attributeName = new AnimateAttribute(node.getAttribute('attributeName') || 'x');
        animate.repeatCount = (node.getAttribute('repeatCount') || 'indefinite');
        animate.duration = parseFloat(node.getAttribute('dur') || '1s');
        animate.subtype = node.getAttribute('type') || 'rotate';
        const keyTimes = node.getAttribute('keyTimes');
        const values = node.getAttribute('values');
        if (keyTimes && values) {
            const ar = keyTimes.split(';');
            const tmepVals = values.split(';');
            for (let i = 0; i < ar.length; i++) {
                animate.timeLine.set(ar[i], tmepVals[i]);
            }
        }

        return animate
    }
}

/**
 * 通用动画组件
 */
export class AnimateObject extends AnimateComObject {
    @panelTitle('变换属性')
    public attributeName: AnimateAttribute = new AnimateAttribute('x');
    constructor() {
        super();
        this.hasChildren = false;
        this.name = '通用动画';
    }
}

export class AnimateMotionObject extends StageObject {
    @panelTitle('变换次数')
    public repeatCount: number | string = 'indefinite';
    @panelTitle('时长/s')
    public duration: number = 5;
    @panelTitle('路径')
    public mpath: SelectObject = new SelectObject('', []);

    constructor() {
        super();
        this.hasChildren = false;
        this.name = '路径动画';
        this.mpath.type = 'refs';
    }

    public set new_mpath(t: string) {
        this.mpath.value = t;
    }

    public static fromNode(node: SVGElement) {
        const motion = new AnimateMotionObject();
        // motion.mpath = node.getAttribute('path');
        motion.repeatCount = (node.getAttribute('repeatCount') || 'indefinite');
        motion.duration = Number(node.getAttribute('dur'));
        return motion;
    }
}

export class AnimateTransformObject extends AnimateComObject {
    public attributeName = 'transform';
    public attributeType: string = 'XML';
    @panelTitle('变换类型')
    public transformType: SelectObject = new SelectObject('rotate', [
        {
            title: '旋转',
            value: 'rotate'
        },
        {
            title: '缩放',
            value: 'scale'
        },
        {
            title: '位移',
            value: 'translate'
        }
    ]);
    @panelTitle('起始状态')
    public from: MultipleValueObject = new MultipleValueObject(new TransformType(this.transformType.value).vals)
    @panelTitle('结束状态')
    public to: MultipleValueObject = new MultipleValueObject(new TransformType(this.transformType.value).vals)
    @panelTitle('循环次数')
    public repeatCount: number | string = 'indefinite';
    @panelTitle('持续时间/s')
    public duration: number = 5;
    constructor() {
        super();
        this.hasChildren = false;
        this.name = '变换动画';
    }
    get from_vals() {
        return this.transformType.vals;
    }
    get to_vals() {
        return this.transformType.vals;
    }
    public set new_transformType(t: string) {
        this.transformType.value = t;
        this.from.vals = new TransformType(t).vals;
        this.to.vals = new TransformType(t).vals;
    }

    get fromValue() {
        let s = ''
        if (this.transformType.value === 'rotate') {
            s = this.from.getVal('a') + ' ' + (this.from.getVal('x')) + ' ' + (this.from.getVal('y'));
        } else if (this.transformType.value === 'translate') {
            s = this.from.getVal('x') + ' ' + this.from.getVal('y');
        } else if (this.transformType.value === 'scale') {
            s = (this.from.getVal('x')) + ' ' + (this.from.getVal('y'));
        }
        return s
    }

    get toValue() {
        let s = ''
        if (this.transformType.value === 'rotate') {
            s = this.to.getVal('a') + ' ' + (this.to.getVal('x')) + ' ' + (this.to.getVal('y'));
        } else if (this.transformType.value === 'translate') {
            s = this.to.getVal('x') + ' ' + this.to.getVal('y');
        } else if (this.transformType.value === 'scale') {
            s = (this.to.getVal('x')) + ' ' + (this.to.getVal('y'));
        }
        return s
    }

    public static fromNode(node: SVGElement) {
        const keyTimes = node.getAttribute('keyTimes');
        if (keyTimes) {
            const obj = super.fromNode(node);
            obj.attributeName = 'transform'
            return obj
        }

        const transform = new AnimateTransformObject();
        transform.repeatCount = (node.getAttribute('repeatCount') || 'indefinite');
        transform.duration = parseFloat(node.getAttribute('dur') || '3s');
        transform.attributeName = node.getAttribute('attributeName') || 'transform';
        transform.attributeType = node.getAttribute('attributeType') || 'XML';
        transform.transformType.value = node.getAttribute('type') || 'rotate';

        const fromValue = node.getAttribute('from')?.split(' ')
        if (fromValue && fromValue.length == 2) {
            transform.from.setVal('x', fromValue[0]);
            transform.from.setVal('y', fromValue[1]);
        } else if (fromValue && fromValue.length == 3) {
            transform.from.setVal('a', fromValue[0]);
            transform.from.setVal('x', fromValue[1]);
            transform.from.setVal('y', fromValue[2]);
        }
        const toValue = node.getAttribute('to')?.split(' ')
        if (toValue && toValue.length == 2) {
            transform.to.setVal('x', toValue[0]);
            transform.to.setVal('y', toValue[1]);
        } else if (toValue && toValue.length == 3) {
            transform.to.setVal('a', toValue[0]);
            transform.to.setVal('x', toValue[1]);
            transform.to.setVal('y', toValue[2]);
        }

        return transform;
    }
}

export class MotionPath extends StageObject {
    public name = '动画路径';
    @panelTitle('动画路径')
    public href: string = '';
}

/**
 * 变换
 */
export class TransformObject extends MultipleValueListObject {
    public skew: MultipleValueObject = new MultipleValueObject([
        {
            title: 'x',
            type: 'number',
            val: 0
        },
        {
            title: 'y',
            type: 'number',
            val: 0
        }
    ]);
    public rotate = new MultipleValueObject([
        {
            title: 'a',
            type: 'number',
            val: 0
        },
        {
            title: 'x',
            type: 'number',
            val: 0
        },
        {
            title: 'y',
            type: 'number',
            val: 0
        }
    ]);
    public translate = new MultipleValueObject([
        {
            title: 'x',
            type: 'number',
            val: 0
        },
        {
            title: 'y',
            type: 'number',
            val: 0
        }
    ]);
    public scale = new MultipleValueObject([
        {
            title: 'x',
            type: 'number',
            val: 1
        },
        {
            title: 'y',
            type: 'number',
            val: 1
        }
    ]);
    public transformOrigin = new MultipleValueObject([
        {
            title: 'x',
            type: 'number',
            val: 0
        },
        {
            title: 'y',
            type: 'number',
            val: 0
        }
    ]);
    public name = '变换';
    public keys = [
        {
            title: '倾斜',
            key: 'skew'
        },
        {
            title: '旋转',
            key: 'rotate'
        },
        {
            title: '平移',
            key: 'translate'
        },
        {
            title: '缩放',
            key: 'scale'
        },
        {
            title: '基点',
            key: 'transformOrigin'
        }
    ];
    transformToString() {
        const s = `rotate(${this.rotate.getVal('a')},${this.rotate.getVal('x')},${this.rotate.getVal('y')}) 
        translate(${this.translate.getVal("x")},${this.translate.getVal("y")}) 
        scale(${this.scale.getVal("x")},${this.scale.getVal('y')}) 
        skewX(${this.skew.getVal("x")}) skewY(${this.skew.getVal("y")})`
        return s
    }

    static fromTransformString(code: string, origin?: string | null) {
        const transform = new TransformObject();
        const arr = code.split(' ');
        for (let index = 0; index < arr.length; index++) {
            const item = arr[index];
            if (item.includes('rotate')) {
                const val = item.replace(/(rotate|\(|\))/g, '').split(',');
                transform.rotate.setVal('a', val[0]);
                transform.rotate.setVal('x', val[1]);
                transform.rotate.setVal('y', val[2]);

            } else if (item.includes('translate')) {
                const val = item.replace(/(translate|\(|\))/g, '').split(',');
                transform.translate.setVal('x', val[0]);
                transform.translate.setVal('y', val[1]);
            } else if (item.includes('scale')) {
                const val = item.replace(/(scale|\(|\))/g, '').split(',');
                transform.scale.setVal('x', val[0]);
                transform.scale.setVal('y', val[1]);
            } else if (item.includes('skewX')) {
                const val = item.replace(/(skewX|\(|\))/g, '')
                transform.skew.setVal('x', val);
            } else if (item.includes('skewY')) {
                const val = item.replace(/(skewY|\(|\))/g, '')
                transform.skew.setVal('y', val);
            }
        }

        if (origin) {
            transform.transformOrigin.setVal('x', parseFloat(origin.split(' ')[0]) || 0);
            transform.transformOrigin.setVal('y', parseFloat(origin.split(' ')[1]) || 0);
        }

        return transform
    }

    get origin() {
        const x = this.transformOrigin.getVal('x');
        const y = this.transformOrigin.getVal('y');
        if (x == 0 && y == 0) {
            return '0 0';
        }
        return `${this.transformOrigin.getVal('x') + (this.parent?.x || 0)} ${this.transformOrigin.getVal('y') + (this.parent?.y || 0)}`
    }
}

export class FilterMultipleValueObject {
    public select: SelectObject = new SelectObject('', [
        {
            title: '无',
            value: ''
        },
        {
            title: '模糊',
            value: 'blur'
        },
        {
            title: '灰度',
            value: 'grayscale'
        },
        {
            title: '饱和度',
            value: 'saturate'
        },
        {
            title: '透明度',
            value: 'opacity'
        },
        {
            title: '色相',
            value: 'hueRotate'
        }
    ]);
    private keys = {
        blur: [
            {
                type: 'number',
                title: 'px',
                val: 0
            },
        ],
        grayscale: [
            {
                type: 'number',
                title: '%',
                val: 0
            },
        ],
        saturate: [
            {
                type: 'number',
                title: '%',
                val: 100
            },
        ],
        opacity: [
            {
                type: 'number',
                title: '%',
                val: 100
            }
        ],
        hueRotate: [
            {
                type: 'number',
                title: 'deg',
                val: 0
            }
        ]
    }
    public multiple = new MultipleValueObject([]);
    public setMultiple(key: string) {
        this.multiple.vals = Reflect.get(this.keys, key);
    }
    get value() {
        let val = '';
        switch (this.select.value) {
            case 'blur':
                val = Filters.blur(this.multiple.getVal('px'));
                break
            case 'grayscale':
                val = Filters.grayscale(this.multiple.getVal('%'));
                break
            case 'saturate':
                val = Filters.saturate(this.multiple.getVal('%'));
                break
            case 'opacity':
                val = Filters.opacity(this.multiple.getVal('%'));
                break
            case 'hueRotate':
                val = Filters.hueRotate(this.multiple.getVal('deg'));
                break
        }

        return val
    }
}